﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using Silvermoon.OpenGL.Native;

namespace Silvermoon.Utils
{
    internal static class ColorUtil
    {

        /// <summary>
        /// Gets the Color as a result of a background color overlapped by a color with a transparency.
        /// </summary>
        /// <param name="backColor">The background color.</param>
        /// <param name="blendColor">The overlapped color.</param>
        /// <param name="alpha">The alphablend value between 0 and 255.</param>
        /// <returns>A combinded color.</returns>
        public static Color AlphaBlendColor(Color backColor, Color blendColor, int alpha)
        {
            GlColor c1 = backColor;
            GlColor c2 = blendColor;
            byte r = Blend(c2.B, c1.B, alpha);
            byte g = Blend(c2.G, c1.G, alpha);
            byte b = Blend(c2.R, c1.R, alpha);
            GlColor c = new GlColor { A = 255, B = r, G = g, R = b };
            return Color.FromArgb(c.Value);
        }


        private static byte Blend(int value1, int value2, int alpha)
        {
            unchecked
            {
                int d = value1 - value2;
                d = (d * alpha) / 255 + value2;
                if (d < 0) d = 0; else if (d > 255) d = 255;
                return (byte)d;
            }
        }

        public static Color[] CreatePlasticColors(Color color)
        {
            return new Color[]
            {
                AlphaBlendColor(color, Color.White, 96),
                AlphaBlendColor(color, Color.White, 24),
                color,
                AlphaBlendColor(color, Color.Black, 54)
            };
        }

        public static Color[] CreateChromeColors(Color color)
        {
            return new Color[]
            {
                AlphaBlendColor(color, Color.White, 55),
                AlphaBlendColor(color, Color.White, 32),
                color,
                AlphaBlendColor(color, Color.Black, 16),
//                color
            };
        }

        public static Color[] CreateFlatChromeColors(Color color)
        {
            return new Color[]
            {
                AlphaBlendColor(color, Color.White, 140),
                AlphaBlendColor(color, Color.White, 40),
                color,
                AlphaBlendColor(color, Color.Black, 64)
            };
        }


        public static Color[] CreateGradientColor(Color color)
        {
            return new Color[]
            {
                AlphaBlendColor(color, Color.White, 120),
                color,
                color,
                AlphaBlendColor(color, Color.Black, 20)
            };
        }


        /// <summary>
        /// Converts a 24 or less bit color to a 32 bit color with a specific alpha channel.
        /// </summary>
        /// <param name="c">The rgb color to convert.</param>
        /// <param name="alpha">The alpha value for the color.</param>
        /// <returns>The new color with alpha channel.</returns>
        public static Color AlphaColor(Color c, int alpha)
        {
            GlColor color = new GlColor { Value = c.ToArgb() };
            color.A = (byte)alpha;
            return Color.FromArgb(color.Value);
            //unchecked
            //{
            //    uint ualpha = (uint)alpha;
            //    ualpha = ualpha << 24;
            //    int mask = (int)ualpha;
            //    int v = (c.ToArgb() & 0xFFFFFF) | mask;
            //    //int v = c.B | (c.G << 8) | (c.R << 0x10) | mask;

            //    return Color.FromArgb(v);
            //}
        }

        internal static Color AlphaScaleColor(Color c, int alpha)
        {
            GlColor color = new GlColor { Value = c.ToArgb() };
            color.A = (byte)((alpha * color.A) >> 8);
            return Color.FromArgb(color.Value);
        }


        /// <summary>
        /// Converts a 24 or less bit color to a 32 bit color with a specific alpha channel and swaps the red and blue values.
        /// </summary>
        /// <param name="c">The rgb color to convert.</param>
        /// <param name="alpha">The alpha value for the color.</param>
        /// <returns>The new color with alpha channel.</returns>
        public static Color AlphaSwapBRColor(Color c, int alpha)
        {
            unchecked
            {
                GlColor color = new GlColor { Value = c.ToArgb() };
                color.SwapRB();
                color.A = (byte)alpha;
                return Color.FromArgb(color.Value);

                //uint ualpha = (uint)alpha;
                //ualpha = ualpha << 24;
                //int mask = (int)ualpha;
                //int v = c.R | (c.G << 8) | (c.B << 0x10) | mask;

                //return Color.FromArgb(v);
            }
        }


        public static Color[] SingleColor(Color color)
        {
            return new Color[] { color };
        }

        /// <summary>
        /// Interpolates a color between two colors.
        /// </summary>
        /// <param name="color">The start color.</param>
        /// <param name="color2">The end color </param>
        /// <param name="proportion">The proportion between color and color2, multiplied with 256.</param>
        /// <returns>The interpolated color.</returns>
        public static Color InterpolateColor(Color color, Color color2, int proportion)
        {
            unchecked
            {
                int r = InterpolateValue(color.R, color2.R, proportion);
                int b = InterpolateValue(color.B, color2.B, proportion);
                int g = InterpolateValue(color.G, color2.G, proportion);
                int a = InterpolateValue(color.A, color2.A, proportion);

                GlColor gl = new GlColor { A = (byte)a, R = (byte)b, G = (byte)g, B = (byte)r };
                return Color.FromArgb(gl.Value);

                //int value = (int)(((ulong)((((r << 0x10) | (g << 8)) | b) | (a << 0x18))) & 0xffffffffL);
                //return Color.FromArgb(value);
            }
        }

        private static int InterpolateValue(byte a, byte b, int proportion)
        {
            unchecked
            {
                return a + ((b - a) * proportion) / 256;
            }
        }

        /// <summary>
        /// Swaps R value with B value.
        /// </summary>
        /// <param name="argb">argb value</param>
        /// <returns>abgr value</returns>
        public static int SwapRB(int argb)
        {
            GlColor color = new GlColor { Value = argb };
            color.SwapRB();
            return color.Value;
            //unchecked
            //{
            //    int r = (argb >> 16) & 0xff;
            //    int b = argb & 0xff;
            //    return (argb & (int)(0xff00ff00)) | b << 16 | r;
            //}
        }

        public static int ModulateAlpha(int color, int alpha)
        {
            GlColor gl = new GlColor { Value = color };
            gl.A = (byte)((alpha * gl.A) / 255);
            return gl.Value;
            //unchecked
            //{
            //    uint a = (uint)(alpha & 0xFF);
            //    uint c = (uint)color;
            //    return (int)(uint)((c & 0xFFFFFF) | (((c >> 24) * a) & 0xFF00) << 16);
            //}
        }

        public static Color FromArgb(int a, int r, int g, int b)
        {
            GlColor color = new GlColor { A = (byte)a, B = (byte)r, G = (byte)g, R = (byte)b };
            return Color.FromArgb(color.Value);
            //unchecked
            //{
            //    return Color.FromArgb(((((a << 8 + r) << 8) + g) << 8) + b);
            //}
        }

        public static Color GetColor(int r, int g, int b, int a)
        {
            if (a == 0 && b == 0 && r == 0 && g == 0) return Color.Empty;
            GlColor color = new GlColor { A = (byte)a, B = (byte)r, G = (byte)g, R = (byte)b };
            return Color.FromArgb(color.Value);
        }

        public static Color GetColor(byte r, byte g, byte b, byte a)
        {
            if (a == 0 && b == 0 && r == 0 && g == 0) return Color.Empty;
            GlColor color = new GlColor { A = a, B = r, G = g, R = b };
            return Color.FromArgb(color.Value);
        }

        internal static Color Luminance(Color color, int delta)
        {
            GlColor gl = new GlColor { Value = color.ToArgb() };
            gl.B = ColorRange(gl.B + delta);
            gl.G = ColorRange(gl.G + delta);
            gl.R = ColorRange(gl.R + delta);
            return Color.FromArgb(gl.Value);
        }

        private static byte ColorRange(int p)
        {
            if (p < 0) p = 0; else if (p > 255) p = 255;
            return (byte)p;
        }


        internal static Color FromArgb(int rgb)
        {
            return Color.FromArgb(rgb | -16777216);
        }
    }
}
